package it.valeandre.omino.net;

import it.valeandre.omino.model.Matches;
import it.valeandre.omino.model.RSS;
import it.valeandre.omino.model.Ranking;
import it.valeandre.omino.model.Setting;
import it.valeandre.omino.model.TeamsList;
import it.valeandre.omino.utils.ResBundle;
import it.valeandre.omino.utils.Utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.codehaus.jackson.map.ObjectMapper;
import org.simpleframework.xml.core.Persister;

import android.content.Context;


public class NetworkService {

	private static final int BUFFER_SIZE = 16384;
	private static final int CONNECTION_TIMEOUT = 8000;
	private static final int SOCKET_TIMEOUT = 11000;

	private static NetworkService mInstance;
	private ExecutorService mExecutor;

	public static NetworkService getInstance() {
		if (mInstance == null) {
			mInstance = new NetworkService();
		}
		return mInstance;
	}

	private NetworkService() {
		mExecutor = Executors.newFixedThreadPool(3, new ThreadFactory() {

			public Thread newThread(Runnable r) {
				Thread t = new Thread(r);
				t.setPriority(Thread.NORM_PRIORITY);
				return t;
			}
		});
	}

	private abstract class NetworkTask implements Runnable {

		protected NetworkServiceListener mListener;
		protected ObjectMapper mObjectMapper;
		protected DefaultHttpClient mClient;
		protected String mEndpoint;
		protected Persister mSerializer;

		public NetworkTask(NetworkServiceListener listener) {
			mListener = listener;
			mObjectMapper = new ObjectMapper();
			mSerializer = new Persister();
			mClient = new DefaultHttpClient();
//			HttpConnectionParams.setConnectionTimeout(mClient.getParams(), 15000);
			setConnectionTimeout(mClient);
		}

		public void run() {
			runTask();
		}

		protected byte[] loadAll(InputStream input) throws IOException {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();

			byte[] buffer = new byte[BUFFER_SIZE];
			int read;
			while ((read = input.read(buffer)) != -1) {
				baos.write(buffer, 0, read);
			}

			return baos.toByteArray();
		}

		public abstract void runTask();
	}
	
	private void setConnectionTimeout(DefaultHttpClient httpClient) {
		setConnectionTimeout(httpClient, CONNECTION_TIMEOUT, SOCKET_TIMEOUT);
	}
	
	private void setConnectionTimeout(DefaultHttpClient httpClient, int connTimeout, int socketTimeout) {

		connTimeout = connTimeout == -1 ? CONNECTION_TIMEOUT : connTimeout;
		socketTimeout = socketTimeout == -1 ? SOCKET_TIMEOUT : socketTimeout;
		HttpParams httpParameters = new BasicHttpParams();
		HttpConnectionParams.setConnectionTimeout(httpParameters, connTimeout);
		HttpConnectionParams.setSoTimeout(httpParameters, socketTimeout);
		httpClient.setParams(httpParameters);

	}

	private class GetRankingTask extends NetworkTask {

		public GetRankingTask(NetworkServiceListener listener) {
			super(listener);
			mEndpoint = ResBundle.URL_RANKING;
		}

		@Override
		public void runTask() {
			HttpResponse response = null;

			try {
				HttpGet request = new HttpGet(mEndpoint);

				response = mClient.execute(request);
				StatusLine status = response.getStatusLine();

				String content = Utils.convertStreamToString(response.getEntity().getContent());
//				Log.e("OMINO", "Ranking content: " + content);
				

				if (status.getStatusCode() == HttpStatus.SC_OK || status.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
					String table = extractTable(content);
					Ranking ranking = mSerializer.read(Ranking.class, table, false);
					mListener.getRankingPerformed(ranking, table);
				} else {
					mListener.getRankingFailed(status.getStatusCode());
				}

			} catch (Exception e) {
				e.printStackTrace();
				mListener.getRankingFailed(e);
			}

		}

	}

	// lega serie a
	private String extractTable(String html) {
		StringBuilder builder = new StringBuilder(html);
		String startPattern = "<tbody>";
		int start = builder.indexOf(startPattern)/* + startPattern.length() + 1 */;
		int end = builder.indexOf("</tbody>") + 8;
		String ret = builder.substring(start, end).toString();
		ret = ret.replaceAll(" class=\"center\"", "");
		ret = ret.replaceAll(" class=\"right\"", "");
		ret = ret.replaceAll("class", "_class");
		ret = ret.replaceAll("(?s)<tr.*?>", "<tr>");
		ret = ret.replaceAll("&nbsp;", "");

		return ret;
	}

	private class GetTeamFeedTask extends NetworkTask {

		public GetTeamFeedTask(String url, NetworkServiceListener listener) {
			super(listener);
			mEndpoint = url;
		}

		@Override
		public void runTask() {
			HttpResponse response = null;

			try {
				HttpGet request = new HttpGet(mEndpoint);

				response = mClient.execute(request);

				StatusLine status = response.getStatusLine();

                String resp = Utils.convertStreamToString(response.getEntity().getContent());
                Utils.log("RESPONSE:\n" + resp);
                System.out.println("RESPONSE:\n" + resp);

				if (status.getStatusCode() == HttpStatus.SC_OK || status.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
					RSS feed = mSerializer.read(RSS.class, resp, false);
					mListener.getTeamFeedPerformed(mEndpoint, feed);
				} else {
					mListener.getTeamFeedFailed(status.getStatusCode());
				}

			} catch (Exception e) {
				e.printStackTrace();
				mListener.getTeamFeedFailed(e);
			}

		}

	}
	
	private class GetResultsTask extends NetworkTask {

		public GetResultsTask(NetworkServiceListener listener) {
			super(listener);
			mEndpoint = ResBundle.URL_SCORES;
			Utils.log("i", "######################## ");
			Utils.log("i", "######################## ");
			Utils.log("i", "######################## URL SCORES = " + mEndpoint);
			Utils.log("i", "######################## ");
			Utils.log("i", "######################## ");
		}

		@Override
		public void runTask() {
			HttpResponse response = null;

			try {
				HttpGet request = new HttpGet(mEndpoint);

				response = mClient.execute(request);

				StatusLine status = response.getStatusLine();

				if (status.getStatusCode() == HttpStatus.SC_OK || status.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
//					Results results = mSerializer.read(Results.class, response.getEntity().getContent(), false);
//					mListener.getResultsPerformed(results);
					InputStream is = response.getEntity().getContent();
					String data = Utils.convertStreamToString(is);
					Utils.log(data);
					Matches matches = mSerializer.read(Matches.class, data, false);
					mListener.getResultsPerformed(matches);
				} else {
					mListener.getResultsFailed(status.getStatusCode());
				}

			} catch (Exception e) {
				e.printStackTrace();
				mListener.getResultsFailed(e);
//				Log.d("TAG", "Error: " + e.toString());
			}

		}

	}

	//OLD FEEDS
//	private class GetResultsTask extends NetworkTask {
//
//		public GetResultsTask(NetworkServiceListener listener) {
//			super(listener);
//			mEndpoint = ResBundle.URL_SCORES;
//			Utils.log("i", "######################## URL SCORES = " + mEndpoint);
//			Utils.log("i", "######################## URL SCORES = " + mEndpoint);
//			Utils.log("i", "######################## URL SCORES = " + mEndpoint);
//			Utils.log("i", "######################## URL SCORES = " + mEndpoint);
//			Utils.log("i", "######################## URL SCORES = " + mEndpoint);
//		}
//
//		@Override
//		public void runTask() {
//			HttpResponse response = null;
//
//			try {
//				HttpGet request = new HttpGet(mEndpoint);
//
//				response = mClient.execute(request);
//
//				StatusLine status = response.getStatusLine();
//
//				if (status.getStatusCode() == HttpStatus.SC_OK || status.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
//					Results results = mSerializer.read(Results.class, response.getEntity().getContent(), false);
//					mListener.getResultsPerformed(results);
//				} else {
//					mListener.getResultsFailed(status.getStatusCode());
//				}
//
//			} catch (Exception e) {
//				e.printStackTrace();
//				mListener.getResultsFailed(e);
////				Log.d("TAG", "Error: " + e.toString());
//			}
//
//		}
//
//	}
	
	private class GetTmpResultsTask extends NetworkTask {

		public GetTmpResultsTask(NetworkServiceListener listener) {
			super(listener);
			mEndpoint = ResBundle.URL_TMP_SCORES;
			Utils.log("i", "######################## ");
			Utils.log("i", "######################## ");
			Utils.log("i", "######################## URL LIVE SCORES = " + mEndpoint);
			Utils.log("i", "######################## ");
			Utils.log("i", "######################## ");
		}

		@Override
		public void runTask() {
			HttpResponse response = null;

			try {
				HttpGet request = new HttpGet(mEndpoint);

				response = mClient.execute(request);

				StatusLine status = response.getStatusLine();

				if (status.getStatusCode() == HttpStatus.SC_OK || status.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
					InputStream is = response.getEntity().getContent();
					String data = Utils.convertStreamToString(is);
					Utils.log(data);
					Matches matches = mSerializer.read(Matches.class, data, false);
					mListener.getTmpResultsPerformed(matches);
				} else {
					mListener.getTmpResultsFailed(status.getStatusCode());
				}

			} catch (Exception e) {
				e.printStackTrace();
				mListener.getTmpResultsFailed(e);
//				Log.d("TAG", "Error: " + e.toString());
			}

		}

	}

	private class UpdateTokenTask extends NetworkTask {

		private String mDeviceId;
		private String mRegistrationId;

		public UpdateTokenTask(String deviceId, String registrationId, NetworkServiceListener listener) {
			super(listener);
			mEndpoint = ResBundle.URL_TOKEN_UPDATE + "/" + deviceId + "/" + registrationId;
			Utils.log("@@@@@@@@@@@@@@@@@ Update Token URL = " + mEndpoint);
			mDeviceId = deviceId;
			mRegistrationId = registrationId;
		}

		@Override
		public void runTask() {
			HttpResponse response = null;

			try {
				HttpGet request = new HttpGet(mEndpoint);

//				List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
//				nameValuePairs.add(new BasicNameValuePair("deviceid", mDeviceId));
//				nameValuePairs.add(new BasicNameValuePair("registrationid", mRegistrationId));
//				request.setEntity(new UrlEncodedFormEntity(nameValuePairs));

				response = mClient.execute(request);

				StatusLine status = response.getStatusLine();

				if (status.getStatusCode() == HttpStatus.SC_OK || status.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
//					Log.d("TAG", "Http status: " + status.getStatusCode());
					mListener.tokenUpdatePerformed(mRegistrationId);
				} else {
					mListener.tokenUpdateFailed(status.getStatusCode());
//					Log.d("TAG", "Http error: " + status.getStatusCode());
				}

			} catch (Exception e) {
				e.printStackTrace();
				mListener.tokenUpdateFailed(e);
//				Log.d("TAG", "Error: " + e.toString());
			}

		}

	}

//	private class SetNewsPushTask extends NetworkTask {
//
//		public SetNewsPushTask(boolean flag, String team, String registrationId, NetworkServiceListener listener) {
//			super(listener);
//			mEndpoint = ResBundle.URL_NEWS_PUSH + "/" + flag + "/" + team + "/" + registrationId;
//		}
//
//		@Override
//		public void runTask() {
//			HttpResponse response = null;
//
//			try {
//				HttpGet request = new HttpGet(mEndpoint);
//
//				response = mClient.execute(request);
//
//				StatusLine status = response.getStatusLine();
//
//				if (status.getStatusCode() == HttpStatus.SC_OK || status.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
////					Log.d("TAG", "Http status: " + status.getStatusCode());
//					mListener.setNewsPushPerformed();
//				} else {
//					mListener.setNewsPushFailed(status.getStatusCode());
////					Log.d("TAG", "Http error: " + status.getStatusCode());
//				}
//
//			} catch (Exception e) {
//				e.printStackTrace();
//				mListener.setNewsPushFailed(e);
////				Log.d("TAG", "Error: " + e.toString());
//			}
//
//		}
//
//	}
	
	private class GetTeamsTask extends NetworkTask {

		public GetTeamsTask(NetworkServiceListener listener) {
			super(listener);
			mEndpoint = ResBundle.URL_GET_TEAMS;
		}
		
		@Override
		public void runTask() {
		
			try {
				TeamsList teams = Utils.get2014SerieATeams();
				mListener.getTeamsPerformed(teams);
			} catch (Exception e) {
				mListener.getTeamsFailed(e);
			}
		
		}

//		@Override
//		public void runTask() {
//			HttpResponse response = null;
//
//			try {
//				HttpGet request = new HttpGet(mEndpoint);
//
//				response = mClient.execute(request);
//
//				StatusLine status = response.getStatusLine();
//				
//				String content = Utils.convertStreamToString(response.getEntity().getContent());
////				Log.e("OMINO", "teams content: " + content);
//
//				if (status.getStatusCode() == HttpStatus.SC_OK || status.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
//					TeamsList xml = mSerializer.read(TeamsList.class, content, false);
//					mListener.getTeamsPerformed(xml);
//					
//				} else {
//					mListener.getTeamsFailed(status.getStatusCode());
//				}
//
//			} catch (Exception e) {
//				mListener.getTeamsFailed(e);
//			}
//
//		}

	}
	
	private class SetPushTeamsTask extends NetworkTask {
		
		private String mTeamsListString;
		
		public SetPushTeamsTask(String teamsListString, String deviceId, String registrationId, NetworkServiceListener listener) {
			super(listener);
			mTeamsListString = teamsListString.replace(" ", "%20");
			mEndpoint = ResBundle.URL_SET_PUSH_TEAMS + "/" + deviceId + "/" + registrationId + "/" + mTeamsListString;
			Utils.log("i", "SetPushTeamsTask URL = " + mEndpoint);
		}

		@Override
		public void runTask() {
			HttpResponse response = null;

			try {
				HttpGet request = new HttpGet(mEndpoint);

				response = mClient.execute(request);

				StatusLine status = response.getStatusLine();
				
				if (status.getStatusCode() == HttpStatus.SC_OK || status.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
					mListener.setPushTeamsPerformed(mTeamsListString);
				} else {
					mListener.setPushTeamsFailed(status.getStatusCode());
				}

			} catch (Exception e) {
				mListener.setPushTeamsFailed(e);
			}

		}

	}
	
	private class SetPushEnabledTask extends NetworkTask {

		private boolean mEnabled;
		
		public SetPushEnabledTask(boolean flag, String registrationId,String deviceId,NetworkServiceListener listener) {
			super(listener);
			mEnabled = flag;
			mEndpoint = ResBundle.URL_DISABLE_PUSH+"/"+flag+"/"+deviceId+"/"+registrationId;
		}

		@Override
		public void runTask() {
			HttpResponse response = null;

			try {
				HttpGet request = new HttpGet(mEndpoint);

				response = mClient.execute(request);

				StatusLine status = response.getStatusLine();
				
				if (status.getStatusCode() == HttpStatus.SC_OK || status.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
//					Log.d("TAG", "Http status: " + status.getStatusCode());
					mListener.setPushEnabledPerformed(mEnabled);
					
				} else {
					mListener.setPushEnabledFailed(status.getStatusCode());
//					Log.d("TAG", "Http error: " + status.getStatusCode());
				}

			} catch (Exception e) {
				e.printStackTrace();
				mListener.setPushEnabledFailed(e);
//				Log.d("TAG", "Error: " + e.toString());
			}

		}

	}
	
	public void getTeamFeed(String url, NetworkServiceListener listener) {
		GetTeamFeedTask task = new GetTeamFeedTask(url, listener);
		mExecutor.execute(task);
	}
	
	private class GetSettingsTask extends NetworkTask {

		public GetSettingsTask(String registrationId,NetworkServiceListener listener) {
			super(listener);
			mEndpoint = ResBundle.URL_GET_SETTINGS+"/"+registrationId;
			Utils.log("d", "################### SETTINGS ENDPOINT = " + mEndpoint);
		}

		@Override
		public void runTask() {
			HttpResponse response = null;

			try {
				HttpGet request = new HttpGet(mEndpoint);

				response = mClient.execute(request);

				StatusLine status = response.getStatusLine();
				String content = Utils.convertStreamToString(response.getEntity().getContent());
//				Log.d("OMINO", "Settings\n" + content);
				
				if (status.getStatusCode() == HttpStatus.SC_OK || status.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
//					Log.d("TAG", "Http status: " + status.getStatusCode());
					Setting xml = mSerializer.read(Setting.class, content, false);
					mListener.getSettingsPerformed(xml);
					
				} else {
					mListener.getSettingsFailed(status.getStatusCode());
//					Log.d("TAG", "Http error: " + status.getStatusCode());
				}

			} catch (Exception e) {
				e.printStackTrace();
				mListener.getSettingsFailed(e);
//				Log.d("TAG", "Error: " + e.toString());
			}

		}

	}
	
	private class GetFromAssetsForTestTask extends NetworkTask {

		private String mFilename;
		private Context mContext;
		
		public GetFromAssetsForTestTask(Context ctx, String filename, NetworkServiceListener listener) {
			super(listener);
			mContext = ctx;
			mFilename = filename;
			Utils.log("d", "################### GetFromAssetsForTestTask = " + filename);
		}

		@Override
		public void runTask() {

			try {
				InputStream input = mContext.getAssets().open(mFilename);

					Matches matches = mSerializer.read(Matches.class, input, false);
					mListener.getFromAssetsForTestTaskPerformed(matches);
					
			} catch (Exception e) {
				e.printStackTrace();
				mListener.getFromAssetsForTestTaskFailed(e);
			}

		}

	}
	
	public void getFromAssetsForTest(Context context, String filename, NetworkServiceListener listener) {
		GetFromAssetsForTestTask task = new GetFromAssetsForTestTask(context, filename, listener);
		mExecutor.execute(task);
	}
	
	public void setPushEnabled(boolean flag,String registrationId,String deviceId, NetworkServiceListener listener) {
		SetPushEnabledTask task = new SetPushEnabledTask(flag, registrationId,deviceId,listener);
		mExecutor.execute(task);
	}
	
	public void setPushTeams(String teamsListString, String deviceId, String registrationId, NetworkServiceListener listener) {
		SetPushTeamsTask task = new SetPushTeamsTask(teamsListString, deviceId, registrationId, listener);
		mExecutor.execute(task);
	}
	
	public void getRanking(NetworkServiceListener listener) {
		GetRankingTask task = new GetRankingTask(listener);
		mExecutor.execute(task);
	}
	
	public void getTeams(NetworkServiceListener listener) {
		GetTeamsTask task = new GetTeamsTask(listener);
		mExecutor.execute(task);
	}

	public void getResults(NetworkServiceListener listener) {
		GetResultsTask task = new GetResultsTask(listener);
		mExecutor.execute(task);
	}
	
	public void getTmpResults(NetworkServiceListener listener) {
		GetTmpResultsTask task = new GetTmpResultsTask(listener);
		mExecutor.execute(task);
	}

	public void updateToken(String deviceId, String registrationId, NetworkServiceListener listener) {
		UpdateTokenTask task = new UpdateTokenTask(deviceId, registrationId, listener);
		mExecutor.execute(task);
	}
	public void getSettings(String registrationId,NetworkServiceListener listener) {
		GetSettingsTask task = new GetSettingsTask(registrationId,listener);
		mExecutor.execute(task);
	}
	
	public void shutdown() {
		mExecutor.shutdown();
		mInstance = null;
	}

}
